import { IPoint, ToPoint } from './IPoint';
import { IDiagram, IConnector, ILocation, IDiagramLine, IAnchor } from './IDiagram';

const diagram: IDiagram = {
    scheme: [
        {
            name: 's1',
            connector: 'start',
            desc: '开始'
        }, {
            name: 's2',
            connector: 'dicision',
            desc: '是否读缓存'
        }, {
            name: 's3',
            connector: 'process',
            desc: '读缓存'
        }, {
            name: 's4',
            connector: 'process',
            desc: '读数据库'
        }, {
            name: 's5',
            connector: 'terminator',
            desc: '结束'
        }
    ],
    location: [
        {
            name: 's1',
            point: {
                x: 456,
                y: 93
            }
        }, {
            name: 's2',
            point: {
                x: 400,
                y: 200
            }
        }, {
            name: 's3',
            point: {
                x: 400,
                y: 300
            }
        }, {
            name: 's4',
            point: {
                x: 700,
                y: 200
            }
        }, {
            name: 's5',
            point: {
                x: 480,
                y: 516
            }
        }
    ],
    lines: [
        {
            start: 's1',
            sAnchor: 4,
            end: 's2',
            eAnchor: 2,
            p1: [481, 123],
            p4: [480, 200]
        },
        {
            start: 's2',
            sAnchor: 4,
            end: 's3',
            eAnchor: 2,
            p1: [480, 260],
            p4: [480, 300]
        },
        {
            start: 's2',
            sAnchor: 3,
            end: 's4',
            eAnchor: 1,
            p1: [560, 230],
            p4: [700, 230]
        },
        {
            start: 's3',
            sAnchor: 4,
            end: 's5',
            eAnchor: 2,
            p1: [480, 360],
            p4: [480, 501]
        },
        {
            start: 's4',
            sAnchor: 4,
            end: 's5',
            eAnchor: 3,
            p1: [780, 260],
            p2: [785, 514],
            p4: [495, 516]
        }
    ]
};

// 自定义事件
const myEvent = new CustomEvent('my_event', { detail: { clientX: 0, clientY: 0 } });
let theAnchor: IAnchor; // 选中的锚点 
let currentX = 0;
let currentY = 0;
let selectedElement: HTMLElement | SVGPolylineElement = null;
let selectedElementId = "";

let BIGEN_MOVE = false; // 标记鼠标是否按下

const myTool = document.getElementById("myTool");
const canvas = document.getElementById("myCanvas");
// 画节点
function Init() {
    // 画节点
    diagram.location.forEach(ele => {
        const connector = diagram.scheme.find(v => v.name === ele.name);
        drawNode(connector, ele);
    });

    // 画连线
    diagram.lines.forEach(ele => {
        drawLine(ele);
    });
}
Init(); // 立即画

/**
 * 释放之前选中的
 * @param e
 */
function _dismiss(e: MouseEvent) {
    myTool.style.display = "none";
    BIGEN_MOVE = false;
    // 取消节点选中状态
    if (selectedElementId) {
        const lastElement = document.getElementById(selectedElementId);
        lastElement.style.stroke = "#000";
        // 隐藏四角
        for (let i = 0; i < (<HTMLElement>lastElement).parentElement.children.length; i++) {
            const lastitem = <HTMLElement>(<HTMLElement>lastElement).parentElement.children.item(i);
            if (lastitem.getAttribute("data-master")) {
                lastitem.style.display = "none";
            }
        }
    }

    // 取消连线选中状态: 已经选中的行，会有`data-selected`
    const selected_line = document.querySelector("polyline[data-selected='true']");
    if (selected_line) {
        (<HTMLElement>selected_line).style.stroke = "#000";
        selected_line.setAttribute("data-selected", "false");
        selected_line.removeAttribute("marker-start");
        selected_line.setAttribute("marker-end", "url(#arrow)");

        document.getElementById("line-start-handle").remove();
        document.getElementById("line-end-handle").remove();
    }

    const handle1 = document.getElementById("line-handle1");
    const handle2 = document.getElementById("line-handle2");
    if (handle1) {
        handle1.remove();
    }
    if (handle2) {
        handle2.remove();
    }

    if (selectedElement !== null) {
        selectedElement.style.stroke = "#000";
        selectedElement = null;
    }
}

canvas.addEventListener("mousedown", function (e: MouseEvent) {
    _dismiss(e);
}, false);

canvas.addEventListener("mouseup", function (e) {
    BIGEN_MOVE = false;
});

//删除事件
canvas.addEventListener("keydown", function (e) {
    if (e.code.toLocaleLowerCase() === 'delete' && selectedElement != null) {
        // 连线
        if (selectedElement.tagName === 'polyline') {
            selectedElement.remove();
            // 如果有handler，则应该也删除
            document.querySelectorAll("[data-connector='line-handle']").forEach(el => el.remove());
        } else {
            // 开始节点不能删除
            if (selectedElement.getAttribute("data-connector") === 'start') {
                alert("`Start`节点无法删除！");
                return false;
            } else {
                // 连线不直接删除，可以作为新连线使用
                // 再直接删除父级`g`
                selectedElement.parentElement.remove();
                // 最后，重置变量
                selectedElementId = "";
                selectedElement = null;
            }
        }
    }
    return false;
});

// 获取节点的四点坐标
function getAnchors(el: HTMLElement): Object {
    const connector = el.getAttribute("data-connector");
    // <g> transform = "matrix(1, 0, 0, 1, 400, 200)"
    const point = el.parentElement.getAttributeNS(null, "transform").slice(7, -1).split(',').map(v => parseFloat(v));
    let radius = 0, width = 0, height = 0;
    let result = [];
    if (connector === 'start' || connector === 'process' || connector === 'transproc' || connector === 'dicision') {
        width = parseFloat(el.getAttributeNS(null, "width"));
        height = parseFloat(el.getAttributeNS(null, "height"));

        result[0] = [point[4], point[5] + height / 2];
        result[1] = [point[4] + width / 2, point[5]];
        result[2] = [point[4] + width, point[5] + height / 2];
        result[3] = [point[4] + width / 2, point[5] + height];
    } else if (connector === 'terminator') {
        radius = parseFloat(el.getAttributeNS(null, "r"));

        result[0] = [point[4] - radius, point[5]];
        result[1] = [point[4], point[5] - radius];
        result[2] = [point[4] + radius, point[5]];
        result[3] = [point[4], point[5] + radius];
    }

    return result;
}

// 样例
const samples = document.getElementsByClassName("sample")
Array.prototype.forEach.call(samples, function (ele: HTMLElement) {
    ele.addEventListener("mousedown", function (e) {
        e.preventDefault();
        e.stopPropagation();
        const masterId = (<HTMLElement>e.target).closest("#myTool").getAttribute("data-master");
        const masterConnector = (<HTMLElement>e.target).closest("#myTool").getAttribute("data-master-connector");
        if (masterConnector === 'start' || masterConnector === 'terminator') {
            // 开始和结束只能有一个
            const _node = document.querySelector("[data-connector='start']")
            if (_node) {
                alert(('`Start`节点只能存在一个'));
                return false;
            }
        } else if (masterConnector === 'process' || masterConnector === 'transproc') {
            // 这两个节点，只能有一个出
            const _node = document.querySelector("polyline[data-start='" + masterId + "']")
            if (_node) {
                alert(('`Process`和`Transproc`节点只能有一个分支'));
                return false;
            }
        } else if (masterConnector === 'dicision') {
            // `决策`分支允许有两个出
            const _node = document.querySelectorAll("polyline[data-start='" + masterId + "']")
            if (_node && _node.length <= 2) {
                alert(('`Dicision`节点最多有两个分支'));
                return false;
            }
        }

        // 根据锚点不同，向不同方向添加节点
        const newConnector = (<HTMLElement>e.target).getAttribute("data-sample-connector");
        const master = document.getElementById(masterId);
        const anchor = (<HTMLElement>e.target).closest("#myTool").getAttribute("data-anchor");
        const points = getAnchors(master);

        const nodeId = maxId(); // 新节点Id
        const newNode = {
            name: nodeId,
            connector: newConnector,
            desc: '新节点'
        };
        let newItem = {
            name: nodeId,
            point: {
                x: 0,
                y: 0,
            }
        };
        let newLine: IDiagramLine = {
            start: '',
            sAnchor: 0,
            end: '',
            eAnchor: 0,
            p1: [],
            p4: []
        };

        if (anchor === "1") {
            // 左
            newItem = {
                name: nodeId,
                point: {
                    x: points[0][0] - 260,
                    y: points[0][1] - 30, // 这里后期要根据节点类型判断了
                }
            };
            newLine = {
                start: master.getAttributeNS(null, "id"),
                sAnchor: 1,
                end: nodeId,
                eAnchor: 3,
                p1: points[0],
                p4: [points[0][0] - 100, points[0][1]]
            };
        } else if (anchor === "2") {
            // 上
            newItem = {
                name: nodeId,
                point: {
                    x: points[1][0] - 80,
                    y: points[1][1] - 160,
                }
            };
            newLine = {
                start: master.getAttributeNS(null, "id"),
                sAnchor: 2,
                end: nodeId,
                eAnchor: 4,
                p1: points[1],
                p4: [points[1][0], points[1][1] - 100]
            };
        } else if (anchor === "3") {
            // 右
            newItem = {
                name: nodeId,
                point: {
                    x: points[2][0] + 100,
                    y: points[2][1] - 30, // 这里后期要根据节点类型判断了
                }
            };
            newLine = {
                start: master.getAttributeNS(null, "id") + "_3",
                sAnchor: 3,
                end: nodeId,
                eAnchor: 1,
                p1: points[2],
                p4: [points[2][0] + 100, points[2][1]]
            };
        } else if (anchor === "4") {
            // 下
            newItem = {
                name: nodeId,
                point: {
                    x: points[3][0] - 80,
                    y: points[3][1] + 100,
                }
            };
            newLine = {
                start: master.getAttributeNS(null, "id"),
                sAnchor: 4,
                end: nodeId,
                eAnchor: 2,
                p1: points[3],
                p4: [points[3][0], points[3][1] + 100]
            };
        }

        // 画
        drawNode(newNode, newItem);
        drawLine(newLine);

        // 写入主记录
        diagram.scheme.push(newNode);
        diagram.location.push(newItem);
        diagram.lines.push(newLine);
    });
});

/**
 * 析取`SVGPolylineElement`的`points`属性，并返回数组
 * @param el 
 */
function getPoints(el: SVGPolylineElement) {
    let result = [];
    let _points = el.getAttribute("points").split(" ");
    if (_points) {
        _points.forEach(el => {
            // 123,456
            const _r = el.split(',');
            result.push([parseFloat(_r[0]), parseFloat(_r[1])]);
        });

        return result;
    }

    return null;
}

/**
  * 获取某节点所在`g`的坐标transform = matrix()的值
  * @param ele 不是父节点
  */
function getTransformMatrix(ele: HTMLElement) {
    let currentMatrix = [1, 0, 0, 1, 0, 0];
    let _attr = ele.getAttributeNS(null, "transform");
    if (ele.classList.contains('parent')) {
        _attr = ele.parentElement.getAttributeNS(null, "transform");
    }

    const _currentMatrix = _attr.slice(7, -1).split(',');
    for (var i = 0; i < _currentMatrix.length; i++) {
        currentMatrix[i] = parseFloat(_currentMatrix[i]);
    }

    return currentMatrix;
}

/**
 * 画节点
 * @param connector 
 * @param ele 
 * @returns 
 */
function drawNode(connector: IConnector, ele: ILocation) {
    let node: SVGElement = null;
    if (connector.connector === 'start') {
        const width = 50, height = 30;
        node = document.createElementNS("http://www.w3.org/2000/svg", "g");
        node.setAttribute("class", "iconnector");
        node.setAttribute("transform", "matrix(1, 0, 0, 1, " + ele.point.x + ", " + ele.point.y + ")");

        let rectBg = document.createElementNS("http://www.w3.org/2000/svg", "rect");
        rectBg.setAttribute("data-mastr", connector.name);
        rectBg.setAttribute("width", (width + 20).toString());
        rectBg.setAttribute("height", (height + 20).toString());
        rectBg.setAttribute("x", "-10");
        rectBg.setAttribute("y", "-10");
        rectBg.setAttribute("rx", "15");
        rectBg.setAttribute("ry", "15");
        rectBg.setAttribute("style", "fill: #fff; stroke-width: 1; stroke: rgb(0, 0, 0)");
        rectBg.addEventListener("mouseover", function (e) {
            document.querySelectorAll("polygon[data-master='" + connector.name + "']").forEach(el => (<HTMLElement>el).style.display = "block");
        });
        rectBg.addEventListener("mouseout", function (e) {
            document.querySelectorAll("polygon[data-master='" + connector.name + "']").forEach(el => (<HTMLElement>el).style.display = "none");
        });

        let rect = document.createElementNS("http://www.w3.org/2000/svg", "rect");
        rect.setAttribute("id", connector.name);
        rect.setAttribute("data-connector", connector.connector);
        rect.setAttribute("class", "parent");
        rect.setAttribute("width", width.toString());
        rect.setAttribute("height", height.toString());
        rect.setAttribute("rx", "15");
        rect.setAttribute("ry", "15");
        rect.setAttribute("style", "fill: blue; stroke-width: 1; stroke: rgb(0, 0, 0)");

        const a1 = createAnchor(connector, [[-2, 7], [-12, 15], [-2, 23]], 1);
        const a3 = createAnchor(connector, [[52, 7], [62, 15], [52, 23]], 3);
        const a4 = createAnchor(connector, [[17, 32], [25, 42], [32, 32]], 4);

        // node.appendChild(rectBg);
        node.appendChild(rect);
        node.appendChild(a1);
        node.appendChild(a3);
        node.appendChild(a4);
    } else if (connector.connector === 'terminator') {
        node = document.createElementNS("http://www.w3.org/2000/svg", "g");
        node.setAttribute("class", "iconnector");
        node.setAttribute("transform", "matrix(1, 0, 0, 1, " + ele.point.x + ", " + ele.point.y + ")");
        let circle = document.createElementNS("http://www.w3.org/2000/svg", "circle");
        circle.setAttribute("id", connector.name);
        circle.setAttribute("data-connector", connector.connector);
        circle.setAttribute("class", "parent");
        circle.setAttribute("r", "16");
        circle.setAttribute("style", "fill: red; stroke-width: 1; stroke: black");

        // 四角
        const a1 = createAnchor(connector, [[-18, -8], [-28, 0], [-18, 8]], 1);
        const a2 = createAnchor(connector, [[-8, -18], [0, -28], [8, -18]], 2);
        const a3 = createAnchor(connector, [[18, 8], [28, 0], [18, -8]], 3);
        const a4 = createAnchor(connector, [[-8, 18], [0, 28], [8, 18]], 4);

        node.appendChild(circle);
        node.appendChild(a1);
        node.appendChild(a2);
        node.appendChild(a3);
        node.appendChild(a4);
    } else if (connector.connector === 'process' || connector.connector === 'transproc') {
        const width = 160, height = 60;
        node = document.createElementNS("http://www.w3.org/2000/svg", "g");
        node.setAttribute("class", "iconnector");
        node.setAttribute("transform", "matrix(1, 0, 0, 1, " + ele.point.x + ", " + ele.point.y + ")");

        // id要加载rect上
        let rect = document.createElementNS("http://www.w3.org/2000/svg", "rect");
        rect.setAttribute("id", connector.name);
        rect.setAttribute("data-connector", connector.connector);
        rect.setAttribute("class", "parent");
        rect.setAttribute("width", width.toString());
        rect.setAttribute("height", height.toString());
        rect.setAttribute("style", "fill: rgb(255, 255, 255); stroke-width: 1; stroke: rgb(0, 0, 0);");

        let text = document.createElementNS("http://www.w3.org/2000/svg", "text");
        text.setAttribute("id", "txt-" + connector.name);
        text.setAttribute("x", "75");
        text.setAttribute("y", "30");
        text.setAttribute("style", "text-anchor:middle;dominant-baseline:middle;");
        text.textContent = connector.desc;

        const a1 = createAnchor(connector, [[-2, 22], [-12, 30], [-2, 38]], 1);
        const a2 = createAnchor(connector, [[72, -2], [80, -10], [88, -2]], 2);
        const a3 = createAnchor(connector, [[162, 22], [172, 30], [162, 38]], 3);
        const a4 = createAnchor(connector, [[72, 62], [80, 72], [88, 62]], 4);

        node.appendChild(rect);
        node.appendChild(text);
        node.appendChild(a1);
        node.appendChild(a2);
        node.appendChild(a3);
        node.appendChild(a4);
    } else if (connector.connector === 'dicision') {
        const width = 160, height = 60;

        node = document.createElementNS("http://www.w3.org/2000/svg", "g");
        node.setAttribute("class", "iconnector");
        node.setAttribute("transform", "matrix(1, 0, 0, 1, " + ele.point.x + ", " + ele.point.y + ")");

        // id要加载polygon上
        let polygon = document.createElementNS("http://www.w3.org/2000/svg", "polygon");
        polygon.setAttribute("id", connector.name);
        polygon.setAttribute("data-connector", connector.connector);
        polygon.setAttribute("class", "parent");
        polygon.setAttribute("width", width.toString());
        polygon.setAttribute("height", height.toString());
        polygon.setAttribute("points", "80,0 160,30 80,60 0,30");
        polygon.setAttribute("style", "fill: rgb(255, 255, 255); stroke-width: 1; stroke: rgb(0, 0, 0);");

        let text = document.createElementNS("http://www.w3.org/2000/svg", "text");
        text.setAttribute("id", "txt-" + connector.name);
        text.setAttribute("x", "80");
        text.setAttribute("y", "30");
        text.setAttribute("style", "text-anchor:middle;dominant-baseline:middle;");
        text.textContent = connector.desc;

        const a1 = createAnchor(connector, [[-2, 22], [-12, 30], [-2, 38]], 1);
        const a2 = createAnchor(connector, [[72, -2], [80, -10], [88, -2]], 2);
        const a3 = createAnchor(connector, [[162, 22], [172, 30], [162, 38]], 3);
        const a4 = createAnchor(connector, [[72, 62], [80, 72], [88, 62]], 4);

        node.appendChild(polygon);
        node.appendChild(text);
        node.appendChild(a1);
        node.appendChild(a2);
        node.appendChild(a3);
        node.appendChild(a4);
    } else {
        console.error(connector.connector + '未实现');
        return false;
    }

    node.addEventListener("click", function (e: MouseEvent) {
        // 先取消之前的选中状态
        _dismiss(e);
        e.stopPropagation();
        if ((<HTMLElement>e.target).classList.contains('iconnector') || (<HTMLElement>e.target).classList.contains('parent')) {
            selectedElement = <HTMLElement>e.target;
            // 设置选中样式
            const _id = selectedElement.getAttribute("id");
            if (selectedElementId && selectedElementId !== _id) {
                const lastElement = document.getElementById(selectedElementId);
                lastElement.style.stroke = "#000";
                // 隐藏四角
                const lastname = (<HTMLElement>lastElement).getAttribute("data-connector");
                for (let i = 0; i < (<HTMLElement>lastElement).parentElement.children.length; i++) {
                    const lastitem = <HTMLElement>(<HTMLElement>lastElement).parentElement.children.item(i);
                    if (lastitem.getAttribute("data-master")) {
                        lastitem.style.display = "none";
                    }
                }
            }
            selectedElementId = _id;
            selectedElement.style.stroke = "#008eff";

            // 显示四角
            const name = selectedElement.getAttribute("data-connector");
            for (let i = 0; i < selectedElement.parentElement.children.length; i++) {
                const item = <HTMLElement>selectedElement.parentElement.children.item(i);
                if (item.getAttribute("data-master")) {
                    item.style.display = "block";
                }
            }

            // 右侧表单 
            const type = <HTMLInputElement>document.getElementById("type");
            type.value = connector.connector;
            const no = <HTMLInputElement>document.getElementById("no");
            no.value = connector.name;
            const desc = <HTMLInputElement>document.getElementById("desc");
            desc.value = connector.desc;
        }
    }, false);

    node.addEventListener("mousedown", function (e: MouseEvent) {
        BIGEN_MOVE = true;
        e.stopPropagation();
        if ((<HTMLElement>e.target).classList.contains('iconnector') || (<HTMLElement>e.target).classList.contains('parent')) {
            selectedElement = <HTMLElement>e.target;
            currentX = e.clientX;
            currentY = e.clientY;

            const currentMatrix = getTransformMatrix(selectedElement);

            canvas.onmousemove = function (e) {
                if (BIGEN_MOVE == true && !!selectedElement && selectedElement.tagName !== 'polyline') {
                    let dx = e.clientX - currentX;
                    let dy = e.clientY - currentY;
                    currentMatrix[4] += dx;
                    currentMatrix[5] += dy;

                    if ((<Element>e.target).classList.contains('parent')) {
                        (<Element>e.target).parentElement.setAttributeNS(null, "transform", "matrix(" + currentMatrix.join(',') + ")");
                    } else {
                        (<Element>e.target).setAttributeNS(null, "transform", "matrix(" + currentMatrix.join(',') + ")");
                    }

                    currentX = e.clientX;
                    currentY = e.clientY;

                    // 以此为起点的连线
                    const name = (<Element>e.target).getAttribute("id");
                    const lines1 = document.querySelectorAll("polyline[data-start='" + name + "']");
                    lines1.forEach(el => {
                        let _points = getPoints(<SVGPolylineElement>el);
                        _points[0] = [_points[0][0] + dx, _points[0][1] + dy];
                        el.setAttribute("points", _points.join(' '));
                    });

                    const lines2 = document.querySelectorAll("polyline[data-end='" + name + "']");
                    lines2.forEach(el => {
                        let _points = getPoints(<SVGPolylineElement>el);
                        if (_points.length === 2) {
                            // 无动点
                            _points[1] = [_points[1][0] + dx, _points[1][1] + dy];
                        } else if (_points.length === 3) {
                            // 1个动点
                            _points[2] = [_points[2][0] + dx, _points[2][1] + dy];
                        } else if (_points.length === 4) {
                            // 2个动点
                            _points[3] = [_points[3][0] + dx, _points[3][1] + dy];
                        }

                        el.setAttribute("points", _points.join(' '));
                    });
                }
            }

            canvas.onmouseup = function (e) {
                canvas.onmousemove = canvas.onmouseup = undefined;
            }
        }
    }, false);

    node.addEventListener("mouseover", function (e: MouseEvent) {
        // 当拖拽连线经过时，要亮起
        // if (BIGEN_MOVE === true && selectedElement.tagName === 'polyline') {
        //     const masterId = (<HTMLElement>e.target).getAttribute("id");
        //     document.querySelectorAll("polygon[data-master='" + masterId + "']").forEach(el => (<HTMLElement>el).style.display = "block");
        // }
    });

    node.addEventListener("mouseout", function (e: MouseEvent) {
        // 当拖拽连线拖出时，要隐藏掉
        if (BIGEN_MOVE === true && selectedElement.tagName === 'polyline') {
            const masterId = (<HTMLElement>e.target).getAttribute("id");
            document.querySelectorAll("polygon[data-master='" + masterId + "']").forEach(el => (<HTMLElement>el).style.display = "none");
        }

        BIGEN_MOVE = false;
        currentX = 0;
        currentY = 0;
    });

    node.addEventListener("mouseup", function (e: MouseEvent) {
        BIGEN_MOVE = false;
        currentX = 0;
        currentY = 0;
    }, false);

    canvas.appendChild(node);
}

/**
 * 画连线
 * @param ele 
 */
function drawLine(ele: IDiagramLine) {
    const id = ele.start + '-' + ele.end;
    const tag = document.getElementById(id);
    if (tag) {
        tag.remove();
    }

    let line = document.createElementNS("http://www.w3.org/2000/svg", "polyline");
    line.setAttribute("id", id);
    line.setAttribute("data-start", ele.start);
    line.setAttribute("data-start-anchor", ele.sAnchor.toString());
    line.setAttribute("data-end", ele.end);
    line.setAttribute("data-end-anchor", ele.eAnchor.toString());
    line.setAttribute("points", ele.p1 + ' ' + (ele.p2 ? (ele.p2 + ' ') : '') + (ele.p3 ? (ele.p3 + ' ') : '') + ele.p4);
    line.setAttribute("style", "fill: none; stroke: black; stroke-width: 2;");
    line.setAttribute("marker-end", "url(#arrow)");
    line.addEventListener("click", function (e) {
        // 先取消之前的选中状态
        _dismiss(e);

        e.stopPropagation();

        selectedElement = <SVGPolylineElement>e.target;
        let _points = getPoints(<SVGPolylineElement>selectedElement); // 一定要从元素中获取，对象是不变化的
        line.setAttribute("data-selected", "true");
        line.style.stroke = "#008eff";
        line.style.strokeWidth = "2";
        // 附加两个动点:连线有4个点，除了头尾，中间两个点就是折点
        let _mousedown = function (el: SVGCircleElement, anchor: number, e: MouseEvent) {
            e.stopPropagation();
            BIGEN_MOVE = true;
            currentX = e.clientX;
            currentY = e.clientY;

            canvas.onmousemove = function (e) {
                // e.stopPropagation();
                if (BIGEN_MOVE === true) {
                    let dx = e.clientX - currentX;
                    let dy = e.clientY - currentY;

                    let _points = getPoints(line);
                    if (anchor === 1) {
                        // 动点1
                        _points[1] = [_points[1][0] + dx, _points[1][1] + dy];
                        line.setAttribute("points", _points.join(" ")); // 重画
                        el.setAttribute("transform", "matrix(1, 0, 0, 1, " + _points[1] + ")");
                    } else if (anchor === 1) {
                        // 动点2
                        _points[2] = [_points[2][0] + dx, _points[2][1] + dy];
                        line.setAttribute("points", _points.join(" ")); // 重画
                        el.setAttribute("transform", "matrix(1, 0, 0, 1, " + _points[2] + ")");
                    } else if (anchor === -1) {
                        // 开始
                        _points[0] = [_points[0][0] + dx, _points[0][1] + dy];
                        line.setAttribute("points", _points.join(" ")); // 重画
                        el.setAttribute("transform", "matrix(1, 0, 0, 1, " + _points[0] + ")");

                        // 触发范围计算
                        myEvent.detail.clientX = e.clientX;
                        myEvent.detail.clientY = e.clientY;
                        document.querySelectorAll("polygon[data-master]").forEach(el => el.dispatchEvent(myEvent));
                    } else if (anchor === -2) {
                        // 结束
                        _points[_points.length - 1] = [_points[_points.length - 1][0] + dx, _points[_points.length - 1][1] + dy];
                        line.setAttribute("points", _points.join(" ")); // 重画
                        el.setAttribute("transform", "matrix(1, 0, 0, 1, " + _points[_points.length - 1] + ")");

                        // 触发范围计算
                        myEvent.detail.clientX = e.clientX;
                        myEvent.detail.clientY = e.clientY;
                        document.querySelectorAll("polygon[data-master]").forEach(el => el.dispatchEvent(myEvent));
                    }

                    currentX = e.clientX;
                    currentY = e.clientY;
                }
            }

            canvas.onmouseup = function (e) {
                canvas.onmousemove = canvas.onmouseup = undefined;
            }
        }

        // 开始
        let start_point = document.createElementNS("http://www.w3.org/2000/svg", "circle");
        start_point.setAttribute("id", 'line-start-handle');
        start_point.setAttribute("data-connector", 'line-handle');
        start_point.setAttribute("transform", "matrix(1, 0, 0, 1, " + _points[0] + ")");
        start_point.setAttribute("r", "3");
        start_point.setAttribute("style", "fill: #fff; stroke-width: 1; stroke: black");
        start_point.addEventListener("mousedown", function (e) {
            _mousedown(start_point, -1, e);
        });
        start_point.addEventListener("mouseup", function (e) {
            if (theAnchor) {
                const midpoints = getNode4MidPoints(theAnchor.name);
                if (midpoints) {
                    // 鼠标弹起后，自动连接节点的中点
                    let _points = getPoints(line);
                    _points[0] = midpoints[theAnchor.anchor - 1];
                    line.setAttribute("points", _points.join(" ")); // 重画
                    start_point.setAttribute("transform", "matrix(1, 0, 0, 1, " + _points[0] + ")");

                    // 回写连线的部分属性
                    line.setAttribute("data-start", theAnchor.name);
                    line.setAttribute("data-start-anchor", theAnchor.anchor.toString());

                    // 隐藏Anchor
                    document.getElementById(theAnchor.name + "-" + theAnchor.anchor).style.display = "none";
                }
            }
        });
        canvas.appendChild(start_point);

        // 结束
        let end_point = document.createElementNS("http://www.w3.org/2000/svg", "circle");
        end_point.setAttribute("id", 'line-end-handle');
        end_point.setAttribute("data-connector", 'line-handle');
        end_point.setAttribute("transform", "matrix(1, 0, 0, 1, " + _points[_points.length - 1] + ")");
        end_point.setAttribute("r", "3");
        end_point.setAttribute("style", "fill: #fff; stroke-width: 1; stroke: black");
        end_point.addEventListener("mousedown", function (e) {
            _mousedown(end_point, -2, e);
        });
        end_point.addEventListener("mouseup", function (e) {
            if (theAnchor) {
                const midpoints = getNode4MidPoints(theAnchor.name);
                if (midpoints) {
                    // 鼠标弹起后，自动连接节点的中点
                    let _points = getPoints(line);
                    _points[_points.length - 1] = midpoints[theAnchor.anchor - 1];
                    line.setAttribute("points", _points.join(" ")); // 重画
                    end_point.setAttribute("transform", "matrix(1, 0, 0, 1, " + _points[_points.length - 1] + ")");

                    // 回写连线的部分属性
                    line.setAttribute("data-end", theAnchor.name);
                    line.setAttribute("data-end-anchor", theAnchor.anchor.toString());

                    // 隐藏Anchor
                    document.getElementById(theAnchor.name + "-" + theAnchor.anchor).style.display = "none";
                }
            }
        });
        canvas.appendChild(end_point);

        // 动点可能有1个，最多2个
        if (_points.length >= 3) {
            // 有三个坐标时，至少1个动点
            let p1 = document.createElementNS("http://www.w3.org/2000/svg", "circle");
            p1.setAttribute("id", 'line-handle1');
            p1.setAttribute("data-connector", 'line-handle');
            p1.setAttribute("transform", "matrix(1, 0, 0, 1, " + _points[1] + ")");
            p1.setAttribute("r", "3");
            p1.setAttribute("style", "fill: #fff; stroke-width: 1; stroke: black");
            p1.addEventListener("mousedown", function (e) {
                _mousedown(p1, 1, e);
            });
            canvas.appendChild(p1);
        }

        if (_points.length === 4) {
            // 有四个坐标时，铁定有2个动点
            let p2 = document.createElementNS("http://www.w3.org/2000/svg", "circle");
            p2.setAttribute("id", 'line-handle2');
            p2.setAttribute("data-connector", 'line-handle');
            p2.setAttribute("transform", "matrix(1, 0, 0, 1, " + _points[2] + ")");
            p2.setAttribute("r", "3");
            p2.setAttribute("style", "fill: #fff; stroke-width: 1; stroke: black");
            p2.addEventListener("mousedown", function (e) {
                _mousedown(p2, 2, e);
            });
            canvas.appendChild(p2);
        }
    });
    line.addEventListener("mouseover", function (e) {
        line.style.strokeWidth = "2";
        line.style.stroke = "#008eff";
    });
    line.addEventListener("mouseout", function (e) {
        if (line.getAttribute("data-selected") !== 'true') {
            line.style.strokeWidth = "2";
            line.style.stroke = "#000";
        }
    });

    canvas.appendChild(line);
}

/**
 * 取Diagram中最大的Id
 */
function maxId() {
    const ids = diagram.scheme.map(v => {
        // s1 -- sn
        return parseInt(v.name.substring(1));
    });

    if (ids && ids.length) {
        return 's' + (ids.sort().slice(-1)[0] + 1);
    } else {
        return 's1';
    }
}

/**
 * 构建节点的四角
 * @param connector 
 * @param points 
 * @param anchor 
 */
function createAnchor(connector: IConnector, points: Array<Array<number>>, anchor: number) {
    // 画三角
    const _a_click = function (anchor: number, ee: MouseEvent) {
        ee.stopPropagation();
        myTool.style.display = "block";
        myTool.style.top = ee.clientY + 'px';
        myTool.style.left = ee.clientX + 'px';
        myTool.setAttribute("data-master", connector.name);
        myTool.setAttribute("data-master-connector", connector.connector);
        myTool.setAttribute("data-anchor", anchor.toString());
    };

    let a1 = document.createElementNS("http://www.w3.org/2000/svg", "polygon");
    a1.setAttribute("id", connector.name + '-' + anchor.toString());
    a1.setAttribute("data-master", connector.name);
    a1.setAttribute("data-anchor", anchor.toString());
    a1.setAttribute("points", points.join(' '));
    a1.setAttribute("style", "fill: lime; stroke: purple; stroke-width: 1; display: none; cursor: pointer;");
    a1.addEventListener("click", e => _a_click(anchor, e));

    a1.addEventListener("mousedown", function (e) {
        BIGEN_MOVE = true;
        currentX = e.clientX;
        currentY = e.clientY;
    });

    a1.addEventListener("mousemove", function (e) {
        e.stopPropagation();
        if (BIGEN_MOVE === true) {
            let dx = e.clientX - currentX;
            let dy = e.clientY - currentY;

            drawLine({
                start: '',
                sAnchor: 3,
                end: '',
                eAnchor: 1,
                p1: [],
                p4: []
            });
        }

        return false;
    });

    a1.addEventListener("mouseup", function (e) {
        BIGEN_MOVE = false;
    });

    a1.addEventListener("mouseover", function (e) {
        // 当拖拽连线经过时，要亮起
        if (BIGEN_MOVE === true && selectedElement.tagName === 'polyline') {
            (<HTMLElement>e.target).style.display = "block";
        }
    });

    a1.addEventListener("mouseout", function (e) {
        // 当拖拽连线经过时，要亮起
        if (BIGEN_MOVE === true && selectedElement.tagName === 'polyline') {
            (<HTMLElement>e.target).style.display = "none";
        }
    });

    // 附加事件
    a1.removeEventListener('my_event', function (e) { });
    a1.addEventListener('my_event', function (e: CustomEvent) {
        const _distance = 20;
        const currentMatrix = getTransformMatrix(document.getElementById(connector.name).parentElement);
        const point = [points[1][0] + currentMatrix[4], points[1][1] + currentMatrix[5]]; // 加上`g`的定位
        let _d = Math.sqrt(Math.pow(point[0] - e.detail.clientX, 2) + Math.pow(point[1] - e.detail.clientY, 2));
        if (_d <= _distance) {
            theAnchor = {
                name: connector.name,
                anchor: anchor
            };
            a1.style.display = "block";
        } else {
            a1.style.display = "none";
            if (theAnchor != null && theAnchor.name === connector.name && theAnchor.anchor === anchor) {
                theAnchor = null;
            }
        }
    });

    return a1;
}

/**
 * 计算得出节点四个中点坐标
 * @param connectorId 
 */
function getNode4MidPoints(connectorId: string) {
    const node = document.getElementById(connectorId);
    if (node) {
        const connector = node.getAttributeNS(null, "data-connector");
        const metrix = getTransformMatrix(node.parentElement);
        if (connector === 'terminator') {
            const r = parseInt(node.getAttributeNS(null, "r")) || 16;

            const p1 = [-1 * r + metrix[4], 0 + metrix[5]];
            const p2 = [0 + metrix[4], -1 * r + metrix[5]];
            const p3 = [r + metrix[4], 0 + metrix[5]];
            const p4 = [0 + metrix[4], r + metrix[5]];

            return [p1, p2, p3, p4];
        } else {
            // 取四角中点
            const width = parseInt(node.getAttributeNS(null, "width")) || 160;
            const height = parseInt(node.getAttributeNS(null, "height")) || 60;

            const p1 = [0 + metrix[4], height / 2 + metrix[5]];
            const p2 = [width / 2 + metrix[4], 0 + metrix[5]];
            const p3 = [width + metrix[4], height / 2 + metrix[5]];
            const p4 = [width / 2 + metrix[4], height + metrix[5]];

            return [p1, p2, p3, p4];
        }
    }
    return null;
}

document.getElementById("btnSave").addEventListener("click", function (e: MouseEvent) {
    const result: IDiagram = {
        scheme: [],
        location: [],
        lines: []
    };

    // scheme + location
    document.querySelectorAll("[data-connector]").forEach(el => {
        const name = el.getAttribute("id");
        const connector = el.getAttribute("data-connector");
        const txt = document.getElementById("txt-" + name);
        // scheme
        result.scheme.push({
            name: name,
            connector: connector,
            desc: txt ? txt.textContent : ""
        });

        // location
        const metrix = getTransformMatrix(<HTMLElement>el);
        result.location.push({
            name: name,
            point: {
                x: metrix[4],
                y: metrix[5]
            }
        });
    });

    // lines
    document.querySelectorAll("polyline[data-start]").forEach(el => {
        const start = el.getAttribute("data-start");
        const start_anchor = el.getAttribute("data-start-anchor");
        const end = el.getAttribute("data-end");
        const end_anchor = el.getAttribute("data-end-anchor");

        // scheme
        const points = getPoints(<SVGPolylineElement>el);
        result.lines.push({
            start: start,
            sAnchor: parseInt(start_anchor),
            end: end,
            eAnchor: parseInt(end_anchor),
            p1: points[0],
            p2: points.length == 3 ? points[1] : null, // 3个点时
            p3: points.length == 4 ? points[2] : null, // 4个点时
            p4: points[points.length - 1]
        });
    });

    console.log(result);
});